package se.webbzon.oschi01.region;

import java.awt.geom.Point2D;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import se.webbzon.oschi01.nbt.NBTException;
import se.webbzon.oschi01.nbt.NBTDataOutputStreamWriter;
import se.webbzon.oschi01.singleton.Singleton;
import se.webbzon.oschi01.singleton.Singletons;
import se.webbzon.oschi01.utils.Writer;
import se.webbzon.oschi01.world.Chunk;
import se.webbzon.oschi01.world.Region;

import com.googlecode.asmack.connection.impl.ZLibOutputStream;

public class RegionWriter implements Singleton, Writer<Region,OutputStream> {
	
	private final int entries = 1024;
	
	private RegionWriter() {};
	
	public void write(Region region, OutputStream os) throws IOException {
		Map<Integer,Chunk> chunkMap = new HashMap<Integer,Chunk>();
		for (int i = 0; i < region.chunks(); i++) {
			Chunk chunk = region.getChunk(i);
			Point2D p = chunk.getCoordinate();
			int px = (int) p.getX() % 32;
			px = px < 0 ? 32 + px : px;
			int pz = (int) p.getY() % 32;
			pz = pz < 0 ? 32 + pz : pz;
			int coordinates = px + 32 * pz;
			if (chunkMap.put(coordinates, chunk) != null)
				throw new IOException("Chunk collision at coordinates." + p);
		}
		
		DataOutputStream dos = new DataOutputStream(os);
		int offset = 2;
		ArrayList<byte[]> chunkDatas = new ArrayList<byte[]>();
		NBTDataOutputStreamWriter writer = Singletons.instance(NBTDataOutputStreamWriter.class);
		
		// Write offsets
		for (int i = 0; i < entries; i++) {
			Chunk chunk = chunkMap.get(i);
			if (chunk == null)
				dos.writeInt(0);
			else try {
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				DataOutputStream cos = null;
				try {
					cos = new DataOutputStream(new ZLibOutputStream(baos));
				} catch (NoSuchAlgorithmException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				writer.write(chunk.getRoot(), cos);
				cos.flush();
				cos.close();
				byte[] chunkData = baos.toByteArray();
				int sectorCount = (5 + chunkData.length + 4095) / 4096; // TODO should 5 be here?
				int location = (offset << 8) | (0x000000FF & sectorCount);
				dos.writeInt(location);
				chunkDatas.add(chunkData);
				offset += sectorCount;
			} catch (NBTException e) {
				throw new IOException(e);
			}
		}
		
		// Write timestamps
		for (int i = 0; i < entries; i++) {
			Chunk chunk = chunkMap.get(i);
			if (chunk == null)
				dos.writeInt(0);
			else 
				dos.writeInt((int) chunk.getTimestamp());
		}
		
		byte[] zeroes = new byte[4095];
		for (byte[] chunkData : chunkDatas) {
			int size = chunkData.length + 5;
			int fill = 4095 - (size-1) % 4096;
			dos.writeInt(chunkData.length + 1);
			dos.writeByte((byte) 2);
			dos.write(chunkData);
			if (fill > 0) 
				dos.write(zeroes, 0, fill);
		}
	}

}
